<template>
  <div>
    <el-upload
        :action="uploadUrl"
        :before-upload="handleBeforeUpload"
        :on-success="handleUploadSuccess"
        :on-error="handleUploadError"
        :class="editorImgUploader"
        style="display: none;"
        name="file"
        :show-file-list="false"
        :headers="headers"
        ref="uploadRef"
        v-if="type == 'url'"
    >
    </el-upload>

    <el-upload
      accept=".mp3, .m4a, .aac, .mp4, .m4v"
      :before-upload="beforeUpload"
      :http-request="upload"
      :show-file-list="false"
      style="display: inline-block"
      class="editor-video-uploader"
    >
    </el-upload>

    <el-dialog
      v-model="dialogVisible"
      :fullscreen="true"
      :show-close="false"
      custom-class="dispute-upload-dialog"
    >
      <div class="center">
        <div class="fz-18 ellipsis">正在上传：{{ fileData.name }}</div>
        <el-progress
          :text-inside="true"
          :stroke-width="16"
          :percentage="percentage"
        />
        <el-button style="margin-top: 10px" @click="cancel">取消上传</el-button>
      </div>
    </el-dialog>

    <div class="editor">
      <quill-editor
        :ref="userInfo[dynamicVariableName]"
        v-model:content="content"
        contentType="html"
        @textChange="(e) => $emit('update:modelValue', content)"
        :options="options"
        :style="styles"
      />
    </div>
  </div>
</template>

<script setup>
import { QuillEditor, Quill } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { getToken } from "@/utils/auth";
import axios from "axios";

const props = defineProps({
  /* 编辑器的内容 */
  modelValue: {
    type: String,
  },
  /* 高度 */
  height: {
    type: Number,
    default: null,
  },
  /* 最小高度 */
  minHeight: {
    type: Number,
    default: null,
  },
  /* 只读 */
  readOnly: {
    type: Boolean,
    default: false,
  },
  /* 上传文件大小限制(MB) */
  fileSize: {
    type: Number,
    default: 5,
  },
  /* 类型（base64格式、url格式） */
  type: {
    type: String,
    default: "url",
  },
  quillEditorRef:{
    type: String,
    default: "quillEditorRef",
  },
  editorImgUploader:{
    type: String,
    default: "editor-img-uploader",
  }
});

const { proxy } = getCurrentInstance();
// 上传的图片服务器地址
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/system/oss/upload");
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const headers = ref({ Authorization: "Bearer " + getToken() });
const videoUrl = ref("");
let dynamicVariableName = props.quillEditorRef;
let userInfo = {};
userInfo[dynamicVariableName] = ref();
const fileData = ref({
  name: "",
  size: 0,
  type: "",
  suffix: "",
  md5: "",
});
const beforeUpload = (file) => {
  const mimeTypes = [
    "audio/mpeg",
    "audio/x-m4a",
    "audio/aac",
    "video/mp4",
    "video/x-m4v",
  ];
  if (!mimeTypes.includes(file.type)) {
    proxy.$modal.msgError({
      type: "error",
      message: "只能上传 MP3、M4A、AAC、MP4、M4V 格式的文件",
      duration: 6000,
    });
    return false;
  }
  if (file.size / 1024 / 1024 / 1024 > 1.5) {
    proxy.$modal.msgError("文件大小不能超过 1.5G");
    proxy.$modal.msgError({
      type: "error",
      message: "文件大小不能超过 1.5G",
      duration: 6000,
    });
    return false;
  }
  return true;
};
const options = ref({
  theme: "snow",
  bounds: document.body,
  debug: "warn",
  modules: {
    clipboard:{
      // 粘贴板，处理粘贴图片  *** 主要是这里
      matchers: [[Node.ELEMENT_NODE, desMatcher]],
    },
    // 工具栏配置
    toolbar: {
      container: [
        ["bold", "italic", "underline", "strike"],       // 加粗 斜体 下划线 删除线
        ["blockquote", "code-block"],                    // 引用  代码块
        [{ list: "ordered" }, { list: "bullet"} ],       // 有序、无序列表
        [{ indent: "-1" }, { indent: "+1" }],            // 缩进
        [{ size: ["small", false, "large", "huge"] }],   // 字体大小
        [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 标题
        [{ color: [] }, { background: [] }],             // 字体颜色、字体背景颜色
        [{ align: [] }],                                 // 对齐方式
        ["clean"],                                       // 清除文本格式
        ["link", "image", "video"]                       // 链接、图片、视频
      ],
      handlers: {
        image: function (value) {
          if (value) {
            // 调用element图片上传
            document.querySelector(`.${props.editorImgUploader}>.el-upload`).click();
          } else {
            Quill.format("image", true);
          }
        },
        video: function (value) {
          if (value) {
            // 调用element视频上传
            document.querySelector(".editor-video-uploader>.el-upload").click();
          } else {
            Quill.format("video", true);
          }
        },
      },
    }
  },
  placeholder: "",
  readOnly: props.readOnly,
});

function desMatcher(node, Delta) {
  let ops = []
  Delta.ops.forEach(op => {
    if (op.insert && typeof op.insert === 'string') {// 如果粘贴了图片，这里会是一个对象，如果是字符串则可以添加
      ops.push({
        insert: op.insert,
      })
    }else{ //给出提醒
      if(op.insert.image){
        // console.log(op.insert,11)
        // let file = this.base64ToFile(op.insert.image)
        // this.$message({
        //   message:'图片正在上传...',
        //   type:'warning',
        //   duration: 2000
        // })
        // this.desUpload({file})
        proxy.$modal.msgError(`禁止复制图片!,请上传图片`);
      }
    }
  })
  Delta.ops = ops
  return Delta
}
const cancel = () => {
  const formData = new FormData();
  formData.append("name", fileData.value.name);
  formData.append("md5", fileData.value.md5);
  dialogVisible.value = false;
  cancelUpload.value = true;
  controller?.abort();
  axios
    .post(baseUrl + "/system/oss/endUpload", formData, {
      headers: {
        "Content-Type": "multipart/form-data",
        Authorization: "Bearer " + getToken(),
      },
    })
    .then(() => {
      proxy.$modal.msgSuccess("取消上传");
    });
};
const styles = computed(() => {
  let style = {};
  if (props.minHeight) {
    style.minHeight = `${props.minHeight}px`;
  }
  if (props.height) {
    style.height = `${props.height}px`;
  }
  return style;
});

const content = ref("");
watch(() => props.modelValue, (v) => {
  if (v !== content.value) {
    content.value = v === null || v === undefined ? "<p></p>" : v;
  }
}, { immediate: true });

// 图片上传成功返回图片地址
function handleUploadSuccess(res, file) {
  // 如果上传成功
  if (res.code == 200) {
    // 获取富文本实例
   
    console.log(userInfo[dynamicVariableName].value)
    console.log(dynamicVariableName)
    console.log(userInfo)
    let quill = toRaw(userInfo[dynamicVariableName].value).getQuill();
    console.log(quill)
    // 获取光标位置
    let length = quill.selection.savedRange.index;
    // 插入图片，res为服务器返回的图片链接地址
    quill.insertEmbed(length, "image", res.data.url);
    // 调整光标到最后
    quill.setSelection(length + 1);
    proxy.$modal.closeLoading();
  } else {
    proxy.$modal.loading(res.msg);
    proxy.$modal.closeLoading();
  }
}

// 图片上传前拦截
function handleBeforeUpload(file) {
  const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
  const isJPG = type.includes(file.type);
  //检验文件格式
  if (!isJPG) {
    proxy.$modal.msgError(`图片格式错误!`);
    return false;
  }
  // 校检文件大小
  if (props.fileSize) {
    const isLt = file.size / 1024 / 1024 < props.fileSize;
    if (!isLt) {
      proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
      return false;
    }
  }
  proxy.$modal.loading("正在上传文件，请稍候...");
  return true;
}

// 图片失败拦截
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
}

let counter = 0;
const getFileMd5 = () => {
  let guid = (+new Date()).toString(32);
  for (let i = 0; i < 5; i++) {
    guid += Math.floor(Math.random() * 65535).toString(32);
  }
  return "wu_" + guid + (counter++).toString(32);
};
const dialogVisible = ref(false);
const cancelUpload = ref(false);
let controller = null;
const chunkSize = 1 * 1024 * 1024; // 切片大小1M
const percentage = ref(0);
// 计算切片数量
const batchUpload = async (fileObj) => {
  console.log('s')
  percentage.value = 0;
  dialogVisible.value = true;
  cancelUpload.value = false;
  const chunkCount = Math.ceil(fileData.value.size / chunkSize); // 切片数量
  // console.log("切片数量", chunkCount);
  fileData.value.md5 = getFileMd5(); // 文件唯一标识
  for (let i = 0; i < chunkCount; i++) {
    if (cancelUpload.value) return;
    const res = await uploadChunkFile(i, fileObj, chunkCount);
    // console.log(res, "分割线--------------", fileData.value);

    if (res && res.data.code === 200 && res.data.data.url) {
      proxy.$modal.msgSuccess({
        message: `${fileData.value.name}上传成功`,
        title: "提示",
      });
      dialogVisible.value = false;
      // 获取富文本实例
      let quill = toRaw(userInfo[dynamicVariableName].value).getQuill();
      // 获取光标位置
      let length = quill.selection.savedRange.index;
      // 插入图片，res为服务器返回的图片链接地址
      quill.insertEmbed(length, "video", res.data.data.url);
      // 调整光标到最后
      quill.setSelection(length + 1);
      // emit("update:modelValue", res.data.data.url);
      // emit("videoUrl", res.data.data.url);
    } else if (res && res.data.code !== 200) {
      dialogVisible.value = false;
      proxy.$modal.msgError({
        message: `${fileData.value.name}上传失败`,
        title: "提示",
      });
    }
  }
};
// 上传切片
const uploadChunkFile = async (i, fileObj, chunkCount) => {
  // console.log(i, fileObj);
  const start = i * chunkSize;
  const end = Math.min(fileData.value.size, start + chunkSize);
  // console.log(start, end);
  const chunkFile = fileObj.slice(start, end);
  // console.log("chunkFile=>", chunkFile);
  const formData = new FormData();
  formData.append("encrypt", "true");
  formData.append("name", fileData.value.name);
  formData.append("md5", fileData.value.md5);
  formData.append("file", chunkFile, String(i + 1));
  formData.append("size", fileObj.size);
  formData.append("chunk", i);
  formData.append("chunks", chunkCount);

  controller = new AbortController();
  return await axios
    .post(baseUrl + "/system/oss/uploadBigFile", formData, {
      headers: {
        "Content-Type": "multipart/form-data",
        Authorization: "Bearer " + getToken(),
      },
      onUploadProgress: (data) => {
        percentage.value = Number(
          (
            (Math.min(fileData.value.size, start + data.loaded) /
              fileData.value.size) *
            100
          ).toFixed(2)
        );
      },
      signal: controller.signal,
    })
    .then((res) => {
      if (res.data.code === 200) {
        return res;
      } else {
        // 关闭弹框
        dialogVisible.value = false;
        // 结束循环
        cancelUpload.value = true;
        proxy.$modal.msgError({
          message: `${fileData.value.name}上传失败`,
          title: "提示",
        });
      }
      // updateUrl(res.data);
    })
    .catch((err) => {
      // 关闭弹框
      dialogVisible.value = false;
      // 结束循环
      cancelUpload.value = true;
      proxy.$modal.msgError({
        message: `${fileData.value.name}上传失败`,
        title: "提示",
      });
    });
};
const upload = async (file) => {
  // console.log(file);
  const fileObj = file.file;
  const formData = new FormData();
  formData.append("file", file.file);
  const nameList = fileObj.name.split(".");
  fileData.value.name = fileObj.name;
  fileData.value.size = fileObj.size;
  fileData.value.type = fileObj.type;
  fileData.value.suffix = nameList[nameList.length - 1];
  batchUpload(fileObj);
};

</script>

<style>
.center {
  color: #fff;
  width: 50%;
  text-align: center;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.editor-img-uploader {
  display: none;
}
.editor-video-uploader {
  display: none;
}
.editor, .ql-toolbar {
  white-space: pre-wrap !important;
  line-height: normal !important;
}
.quill-img {
  display: none;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
  content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
  border-right: 0px;
  content: "保存";
  padding-right: 0px;
}

.ql-snow .ql-tooltip[data-mode="video"]::before {
  content: "请输入视频地址:";
}

.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
  content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
  content: "10px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
  content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
  content: "32px";
}

.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
  content: "文本";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
  content: "标题1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
  content: "标题2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
  content: "标题3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
  content: "标题4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
  content: "标题5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
  content: "标题6";
}

.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
  content: "标准字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
  content: "衬线字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
  content: "等宽字体";
}
</style>
